﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ext.Net;
using Sio.Mdm.Entities;
using System.Linq.Expressions;
using Sio.Mdm.Entities.Linq.Factory;
using System.Web;

namespace Sio.Mdm.Web.Controls
{
    public class GridPanel : Ext.Net.GridPanel
    {
        #region Cache definitions
        private static Dictionary<long, LoadInfo> LoadDataCache = new Dictionary<long, LoadInfo>();
        private static long CacheIdCounter = 1;
        private bool PaggingSet = false;

        abstract class LoadInfo
        {
            public LoadInfo(Expression expression)
            {
                Expression = expression;
            }

            public abstract IEntityCollection Load(int start, int count, string sort, bool asc, string filter);
            public Expression Expression;
        }
        class LoadInfo<EntityType> : LoadInfo where EntityType : IEntity
        {
            public LoadInfo(ISioFilter<EntityType> query)
                : base(query.Expression)
            {
            }

            public override IEntityCollection Load(int start, int count, string sort, bool asc, string filter)
            {
                ISioFilter<EntityType> query = new Filter<EntityType>(Expression);
                
                EntityCollection<EntityType> col = new EntityCollection<EntityType>();

                if (!String.IsNullOrEmpty(sort))
                {
                    if (asc)
                    {
                        query = FilterFactory.AddOrderBy(query, sort);
                    }
                    else
                    {
                        query = FilterFactory.AddOrderByDescending(query, sort);
                    }
                }

                if (!String.IsNullOrEmpty(filter))
                {
                    FilterConditions fc = new FilterConditions(filter);

                    foreach (FilterCondition c in fc.Conditions)
                    {
                        object value = null;

                        switch (c.FilterType)
                        {
                            case FilterType.Boolean:
                                value = c.ValueAsBoolean;
                                break;

                            case FilterType.Date:
                                value = c.ValueAsDate;
                                break;

                            case FilterType.Numeric:
                                value = c.ValueAsInt;
                                break;

                            case FilterType.String:
                                if (c.Value.Contains('*') || c.Value.Contains('?'))
                                {
                                    if (c.Value.Contains('*'))
                                    {
                                        value = c.Value.Replace('*', '%').Replace('?', '_');
                                    }
                                    else
                                    {
                                        value = String.Format("{0}%", c.Value.Replace('?', '_'));                                        
                                    }
                                }
                                else
                                {
                                    value = String.Format("%{0}%", c.Value);
                                }
                                break;
                        }

                        switch(c.Comparison)
                        {
                            case Comparison.Eq:
                                query = FilterFactory.AddPropertyMatchCaseInsensitive(query, c.Name, value);
                                break;
                            case Comparison.Gt:
                                query = FilterFactory.AddPropertyMoreThen(query, c.Name, value);
                                break;
                            case Comparison.Lt:
                                query = FilterFactory.AddPropertyLessThen(query, c.Name, value);
                                break;
                        }
                    }
                }

                col.Select(query, start, count);
                
                return col;
            }
        }
        #endregion

        public GridPanel()
        {
            EstimatedRowHeight = 24;
            EnableRowResize = true;
        }

        #region Properties
        public int EstimatedRowHeight { get; set; }
        public bool EnableRowResize { get; set; }
        private PageProxy PagingProxy
        {
            get
            {
                return InternalStore.Proxy[0] as PageProxy;
            }
        }
        private string _internalStoreID;
        private string InternalStoreID
        {
            get
            {
                if (String.IsNullOrEmpty(_internalStoreID))
                {
                    if (String.IsNullOrEmpty(StoreID))
                    {
                        _internalStoreID = Store.Primary == null ? null : Store.Primary.ID;
                    }
                    else
                    {
                        Store store = FindControl(StoreID) as Store;
                        _internalStoreID = store == null ? null : store.ID;
                    }
                }

                return _internalStoreID;
            }
        }
        private Store _internalStore;
        private Store InternalStore
        {
            get
            {
                if (null == _internalStore)
                {
                    _internalStore = FindControl(InternalStoreID) as Store;

                    if (_internalStore.Proxy.Count == 0)
                    {
                        _internalStore.Proxy.Add(new PageProxy());
                    }

                    //_internalStore.AutoSave = true;
                }

                return _internalStore;
            }
        }                
        private QueryRange DefaultRange
        {
            get
            {
                return new QueryRange(1, 30);
            }
        }
        private long _cacheUniqueId = CacheIdCounter++;
        private long CacheUniqueId
        {
            get
            {
                if (CacheIdHolder.Value == null)
                {
                    CacheIdHolder.Value = (CacheIdCounter++).ToString();
                }

                return long.Parse(CacheIdHolder.Value.ToString());
            }
        }
        private TextField _cacheIdHolder;
        private TextField CacheIdHolder
        {
            get
            {
                if (_cacheIdHolder == null)
                {
                    SetupPaging();
                }

                return _cacheIdHolder;
            }
        }
        #endregion

        #region Methods

        #region Public

        #region LoadData
        // TODO: Remove generic parameter and take it from ISioFilter<T>
        public void LoadData<EntityType>(ISioFilter<EntityType> query) where EntityType : IEntity
        {
            LoadInfo info = new LoadInfo<EntityType>(query);
            
            LoadDataCache[CacheUniqueId] = info;
                        
            IEntityCollection col = info.Load(
                DefaultRange.Start, 
                DefaultRange.Count, 
                InternalStore.SortInfo.Field,
                InternalStore.SortInfo.Direction != Ext.Net.SortDirection.DESC,
                null
            );
            
            PagingProxy.Total = col.TotalCount;

            InternalStore.DataSource = col;
            InternalStore.DataBind();
        }        
        #endregion 

        #endregion

        #region Protected
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!ExtNet.IsAjaxRequest)
            {   
                SetupStore();
            }

            SetupPaging();
            HookupEvents();
        }
        protected override void OnPreRender(EventArgs e)
        {
            if (!ExtNet.IsAjaxRequest)
            {
                CustomConfig.Add(new ConfigItem(
                    "rowHeight", EstimatedRowHeight.ToString(), ParameterMode.Raw
                ));
                CustomConfig.Add(new ConfigItem(
                    "enableRowResize", EnableRowResize.ToString().ToLower(), ParameterMode.Raw
                ));
                CustomConfig.Add(new ConfigItem("cacheId", CacheUniqueId.ToString(), ParameterMode.Raw));
            }

            base.OnPreRender(e);
        }
        #endregion

        #region Private

        #region Paging & Store setup
        private void SetupPaging()
        {
            if (PaggingSet)
            {
                return;
            }

            PagingToolbar toolbar = new PagingToolbar();

            toolbar.Items.Add(new ToolbarFill() { ID = String.Format("{0}_TF", ID) });
            toolbar.Items.Add(new ToolbarSpacer(10) { ID = String.Format("{0}_TS", ID) });
            toolbar.Items.Add(_cacheIdHolder = new TextField() { ID = String.Format("{0}_CH", ID), Hidden = true });

            toolbar.ID = String.Format("{0}_PagingBar", ID);
            toolbar.StoreID = InternalStoreID;
            toolbar.DisplayInfo = false;
            toolbar.PageSize = 30;

            if (BottomBar.Count == 0)
            {
                BottomBar.Add(toolbar);
            }

            if (View.Count == 0)
            {
                GridView view = new GridView();
                view.ID = String.Format("{0}_View", ID);
                View.Add(view);
            }

            View[0].MarkDirty = false;
            View[0].SplitHandleWidth = 10;

            PaggingSet = true;
        }
        private void SetupStore()
        {
            if (InternalStore == null)
            {
                return;
            }

            InternalStore.RemotePaging = true;
            InternalStore.RemoteSort = true;
            
            InternalStore.BaseParams.Add(new Parameter("start", "0", ParameterMode.Raw));            
            InternalStore.Listeners.Load.Handler = "this.commitChanges();";
        }
        private void HookupEvents()
        {
            if (InternalStore == null)
            {
                return;
            }

            InternalStore.RefreshData += OnRefreshData;
            RowEditor editor = Plugins.
                Where(p => p.GetType() == typeof(RowEditor)).FirstOrDefault()
                as RowEditor;

            if (editor != null)
            {
                editor.Listeners.AfterEdit.Handler +=
                    String.Format("{0}.commitChanges();", InternalStoreID);
            }
        } 
        #endregion

        #region OnRefreshData
        private void OnRefreshData(object sender, StoreRefreshDataEventArgs e)
        {
            //bymatejg, 12.3.2011, če kliknem na seznamu organizacij na podrobnosti pride cacheId null
            if (e.Parameters["cacheId"] == null)
                return;
            //end by matejg

            long cacheId = long.Parse(e.Parameters["cacheId"]);

            if (!LoadDataCache.ContainsKey(cacheId))
            {
                cacheId = CacheUniqueId;
            }

            if (!LoadDataCache.ContainsKey(cacheId))
            {
                return;
            }

            LoadInfo info = LoadDataCache[CacheUniqueId];
            IEntityCollection col = info.Load(e.Start, e.Limit, e.Sort, e.Dir != Ext.Net.SortDirection.DESC, e.Parameters["gridfilters"]);
            
            e.Total = col.TotalCount;

            InternalStore.DataSource = col;
            InternalStore.DataBind();
        }   
        #endregion

        #endregion

        #endregion
    }
}
